﻿using StarsAdmin.Core.Enums;
using System.Linq.Expressions;
using System.Reflection;

namespace StarsAdmin.Core.Extensions
{
    public static class ExpressionExtension
    {
        /// <summary>
        /// 附加
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="appendExpression"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static Expression Append(this Expression expression, Expression appendExpression, Condition condition = Condition.AndAlso)
        {
            if (expression == null)
            {
                return appendExpression;
            }
            return condition == Condition.AndAlso ? Expression.AndAlso(expression, appendExpression) : Expression.OrElse(expression, appendExpression);
        }

        /// <summary>
        /// 包含
        /// </summary>
        /// <param name="member"></param>
        /// <param name="constant"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression Contains(Expression member, Expression constant)
        {
            if (member.Type != typeof(string) || constant.Type != typeof(string))
            {
                throw new ArgumentException("包含运算符仅对字符串类型有效。");
            }
            MethodInfo containsMethod = typeof(string).GetMethod("Contains", [typeof(string)]);
            Expression contains = Expression.Call(member, containsMethod, constant);
            return contains;
        }

        /// <summary>
        /// 不包含
        /// </summary>
        /// <param name="member"></param>
        /// <param name="constant"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression NotContains(Expression member, Expression constant)
        {
            return Expression.Not(Contains(member, constant));
        }

        /// <summary>
        /// 以什么开始
        /// </summary>
        /// <param name="member"></param>
        /// <param name="constant"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression StartsWith(Expression member, Expression constant)
        {
            if (member.Type != typeof(string) || constant.Type != typeof(string))
            {
                throw new ArgumentException("以什么开始运算符仅对字符串类型有效。");
            }
            MethodInfo startWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
            Expression startsWith = Expression.Call(member, startWithMethod, constant);
            return startsWith;
        }

        /// <summary>
        /// 以什么结束
        /// </summary>
        /// <param name="member"></param>
        /// <param name="constant"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression EndsWith(Expression member, Expression constant)
        {
            if (member.Type != typeof(string) || constant.Type != typeof(string))
            {
                throw new ArgumentException("以什么结束运算符仅对字符串类型有效。");
            }
            MethodInfo endWithMethod = typeof(string).GetMethod("EndsWith", [typeof(string)]);
            Expression endsWith = Expression.Call(member, endWithMethod, constant);
            return endsWith;
        }

        /// <summary>
        /// 在范围以内
        /// </summary>
        /// <param name="member"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression Range(Expression member, Expression start, Expression end)
        {
            if (start == null || end == null)
            {
                throw new ArgumentException("在范围以内运算符需要两个值。");
            }
            return Expression.AndAlso(Expression.GreaterThanOrEqual(member, start), Expression.LessThanOrEqual(member, end));
        }

        /// <summary>
        /// 在范围以外
        /// </summary>
        /// <param name="member"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static Expression OutRange(Expression member, Expression start, Expression end)
        {
            if (start == null || end == null)
            {
                throw new ArgumentException("在范围以外运算符需要两个值。");
            }
            return Expression.OrElse(Expression.GreaterThan(member, start), Expression.GreaterThan(member, end));
        }

        /// <summary>
        /// 值类型转换
        /// </summary>
        /// <param name="memberType"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static ConstantExpression GetConstant(Type memberType, object value)
        {
            if (value == null && Nullable.GetUnderlyingType(memberType) != null)
            {
                return Expression.Constant(null, memberType);
            }

            if (memberType == typeof(string) && value is long)
            {
                return Expression.Constant(value.ToString(), memberType);
            }

            if (memberType.IsValueType && value is string stringValue)
            {
                Type underlyingType = Nullable.GetUnderlyingType(memberType) ?? memberType;
                if (underlyingType.IsEnum)
                {
                    try
                    {
                        object convertedValue = Enum.Parse(underlyingType, stringValue, true);
                        return Expression.Constant(convertedValue, memberType);
                    } catch (Exception ex)
                    {
                        throw new ArgumentException($"Cannot convert '{stringValue}' to type {underlyingType.Name}", ex);
                    }
                }
                try
                {
                    object convertedValue = Convert.ChangeType(stringValue, underlyingType);
                    return Expression.Constant(convertedValue, memberType);
                } catch (Exception ex)
                {
                    throw new ArgumentException($"Cannot convert '{stringValue}' to type {underlyingType.Name}", ex);
                }
            }

            return Expression.Constant(value, memberType);
        }
    }
}